<?php
// +----------------------------------------------------------------------
// | INPHP
// | Copyright (c) 2023 https://inphp.cc All rights reserved.
// | Licensed ( https://opensource.org/licenses/MIT )
// | Author: 幺月儿(https://gitee.com/lulanyin) Email: inphp@qq.com
// +----------------------------------------------------------------------
// | 路由状态
// +----------------------------------------------------------------------
namespace Inphp\Core\Object;

use Inphp\Core\Config;
use Inphp\Core\Context;
use Inphp\Core\Services\Http\Response;
use Inphp\Core\Util\Str;

class RouterStatus
{
    /**
     * 响应状态，目前只处理 200，403、404
     * @var int
     */
    public int $status = 100;

    /**
     * 消息，在发生错误的时候，可以加上
     * @var string
     */
    public string $message = "ok";

    /**
     * 自定义数据...按需处理
     * @var string
     */
    public string $state = "controller";

    /**
     * 控制器类和执行的方法名
     * @var array|null
     */
    public array|null $controller = null;

    /**
     * 请求方式
     * @var string|null
     */
    public ?string $method = null;

    /**
     * 视图文件夹
     * @var string|null
     */
    public ?string $viewDir = null;

    /**
     * 视图文件名，并非全路径，相对版块路径
     * @var string|null
     */
    public ?string $view = null;

    /**
     * 进入的版块路径
     * @var string
     */
    public string $path = "";

    /**
     * 请求的 uri
     * @var string
     */
    public string $uri = "";

    /**
     * 进的哪个模块
     * @var string
     */
    public string $module = "";

    /**
     * 内容响应的格式
     * @var string
     */
    public string $responseContentType = "text/html";

    /**
     * 请求时 header 必须包含的内容
     * @var array
     */
    private array $accessControlAllowHeaders = [];

    /**
     * 允许的请求方式
     * @var array
     */
    private array $accessControlAllowMethods = [];

    /**
     * 允许的跨域域名
     * @var array
     */
    private array $accessControlAllowOrigins = [];

    /**
     * Status constructor.
     * @param array $values
     */
    public function __construct(array $values)
    {
        $this->status       = $values["status"] ?? 100;
        $this->message      = $values["message"] ?? "ok";
        $this->state        = $values["state"] ?? "controller";
        $this->controller   = $values["controller"] ?? null;
        $this->method       = $values["method"] ?? null;
        $this->viewDir      = $values["viewDir"] ?? null;
        $this->view         = $values["view"] ?? null;
        $this->path         = $values["path"] ?? "";
        $this->uri          = $values["uri"] ?? "";
        $this->module       = $values["module"] ?? "";
        $this->responseContentType = $values["responseContentType"] ?? "text/html";
        $this->accessControlAllowHeaders = $values["accessControlAllowHeaders"] ?? [];
        $this->accessControlAllowMethods = $values["accessControlAllowMethods"] ?? [];
        $this->accessControlAllowOrigins = $values["accessControlAllowOrigins"] ?? [];
    }

    /**
     * 设置咋就内容类型
     * @param string $type
     * @return RouterStatus
     */
    public function setResponseContentType(string $type): RouterStatus
    {
        $this->responseContentType = $type;//in_array($type, Response::CONTENT_TYPES) ? $type : "text/html";
        return $this;
    }

    /**
     * 设置允许的 header
     * header中必须包含某个键值才能允许响应
     * @param string|array $headers
     * @return RouterStatus
     */
    public function setAccessControlAllowHeaders(string|array $headers): RouterStatus
    {
        $this->accessControlAllowHeaders = is_array($headers) ? $headers : [$headers];
        return $this->trimValue($this->accessControlAllowHeaders);
    }

    /**
     * 添加允许的 header
     * @param string|array $headers
     * @return RouterStatus
     */
    public function addAccessControlAllowHeaders(string|array $headers): RouterStatus
    {
        $this->accessControlAllowHeaders = array_merge($this->accessControlAllowHeaders, is_array($headers) ? $headers : [$headers]);
        return $this->trimValue($this->accessControlAllowHeaders);
    }

    /**
     * 获取允许的 header
     * @return array
     */
    public function getAccessControlAllowHeaders(): array
    {
        return !empty($this->accessControlAllowHeaders) ? $this->accessControlAllowHeaders : ["*"] ;
    }

    /**
     * 检查 header 是否符合
     * @param array $headers    所有客户端当前携带的header，不可包含值，仅取键名，外部调用时必须处理好
     * @return bool
     */
    public function checkHeaders(array $headers): bool
    {
        $bool = true;
        if (!empty($this->accessControlAllowHeaders) && !in_array("*", $this->accessControlAllowHeaders)) {
            foreach ($this->accessControlAllowHeaders as $header) {
                $bool = $bool && in_array($header, $headers);
            }
        }
        return $bool;
    }

    /**
     * 设置允许的请求方式
     * @param string|array $methods
     * @return RouterStatus
     */
    public function setAccessControlAllowMethods(string|array $methods): RouterStatus
    {
        $this->accessControlAllowMethods = is_array($methods) ? $methods : [$methods];
        return $this->trimValue($this->accessControlAllowMethods);
    }

    /**
     * 添加允许的请求方式
     * @param string|array $methods
     * @return RouterStatus
     */
    public function addAccessControlAllowMethods(string|array $methods): RouterStatus
    {
        $this->accessControlAllowMethods = array_merge($this->accessControlAllowMethods, is_array($methods) ? $methods : [$methods]);
        return $this->trimValue($this->accessControlAllowMethods);
    }

    /**
     * 获取允许的请求方式
     * @return array
     */
    public function getAccessControlAllowMethods(): array
    {
        return !empty($this->accessControlAllowMethods) ? $this->accessControlAllowMethods : ["*"] ;
    }

    /**
     * 检测请求方式是否符合
     * @param string|null $method
     * @return bool
     */
    public function checkMethod(?string $method = null): bool
    {
        $method = $method ?? $this->method;
        if (empty($method)) {
            return false;
        }
        if (!empty($this->accessControlAllowMethods) && !in_array("*", $this->accessControlAllowMethods)) {
            return in_array($method, $this->accessControlAllowMethods);
        }
        return true;
    }

    /**
     * 设置允许的请求来源
     * @param string|array $origins
     * @return RouterStatus
     */
    public function setAccessControlAllowOrigins(string|array $origins): RouterStatus
    {
        $this->accessControlAllowOrigins = is_array($origins) ? $origins : [$origins];
        return $this->trimValue($this->accessControlAllowOrigins);
    }

    /**
     * 添加允许的跨域来源
     * @param string|array $origins
     * @return RouterStatus
     */
    public function addAccessControlAllowOrigins(string|array $origins): RouterStatus
    {
        $this->accessControlAllowOrigins = array_merge($this->accessControlAllowOrigins, is_array($origins) ? $origins : [$origins]);
        return $this->trimValue($this->accessControlAllowOrigins);
    }

    /**
     * 检测域名来源是否合法
     * @param string $origin
     * @return bool
     */
    public function checkOrigin(?string $origin): bool
    {
        if (!empty($origin) && !in_array("*", $this->accessControlAllowOrigins)) {
            //取所有域名
            $allHosts = Config::get("accessOrigins", []);
            if (empty($allHosts)) {
                $client = Context::getClient();
                //所有模块绑定的域名、和主域名
                $mainHost = Config::get("domain.main", $client->host);
                $mainHost = Str::deletePrefix($mainHost, ["http://", "https://"]);
                $allHosts[] = $mainHost;
                $modulesDomains = Config::get("modules.domains", []);
                if (!empty($modulesDomains)) {
                    foreach ($modulesDomains as $md) {
                        $allHosts[] = Str::deletePrefix($md, ["http://", "https://"]);
                    }
                }
                //保存
                Config::set("accessOrigins", $allHosts);
            }
            $hostList = array_merge($this->accessControlAllowOrigins, $allHosts);
            //判断
            $origin = Str::deletePrefix($origin, ["http://", "https://"]);
            return in_array($origin, $hostList);
        }
        return true;
    }

    /**
     * 清除空格
     * @param array $array
     * @return RouterStatus
     */
    private function trimValue(array &$array): RouterStatus
    {
        foreach ($array as &$val) {
            $val = trim($val);
        }
        return $this;
    }
}